//
//  PopoverView.swift
//  ZZUI
//
//  Created by 张文晏 on 2018/11/9.
//  Copyright © 2018 张文晏. All rights reserved.
//

import UIKit

public class PopoverView: UIView {
    
    // MARK: - 显示风格
    enum ShowStyle {
        case base // 基本类型，添加自定义 View
        case tableView // Table view 累次那个，添加 Cell
    }
    
    // MARK: - 弹窗箭头
    struct PopoverViewArrow {
        /// 箭头指向方向 (True: 上 / Flase: 下)
        var isUpward = true
        ///  箭头高度
        var height: CGFloat = 3
        /// 箭头最宽处宽度
        var width: CGFloat = 6
        /// 箭头尖圆角
        var cornerRadius: CGFloat = 1
        /// 箭头尾圆角
        var bottomCornerRadius: CGFloat = 1
        /// 箭头距指向点的间距
        var margin: CGFloat = 0
    }
    
    // MARK: - Setting
    /// 是否开启点击外部隐藏弹窗， 默认开启
    public var hideAfterTouchOutside = true
    /// 是否显示背部阴影，默认显示
    public var showShadow = true
    /// 阴影颜色
    public var shadowColor = UIColor(white: 0, alpha: 0.3)
    /// 行高
    public var cellHeight: CGFloat = 40
    /// 圆角
    public var cornerRadius: CGFloat = 3
    /// 宽度
    public var viewWidth: CGFloat?
    /// 高度
    public var viewHeight: CGFloat?
    /// Content view 上下间隔
    public var contentTBMargin: CGFloat = 0
    /// 弹窗外边距
    public var viewMargin: CGFloat = 12
    /// 隐藏视图时的回调
    public var hideClosure: (() -> Void)?
    /// 文本字体
    public var textFont = UIFont.systemFont(ofSize: 14)
    /// 文本颜色
    public var textColor = UIColor.black
    
    /// 屏幕
    private let keyWindow = UIApplication.shared.keyWindow!
    
    // MARK: - Subviews
    private var contentView = UIView()
    /// 列表项 Table view
    private let tableView = UITableView()
    /// 遮罩层
    private let shadowView = UIView()
    /// 箭头
    private var arrow = PopoverViewArrow()
    
    // MARK: - Variables
    /// 显示风格
    private var style = ShowStyle.base
    /// 窗口宽度
    private let windowWidth = UIApplication.shared.keyWindow!.bounds.size.width
    /// 窗口高度
    private let windowHeight = UIApplication.shared.keyWindow!.bounds.size.height
    /// 显示项
    private var actions = [PopoverAction]()
    
    private var currentX: CGFloat = 0
    private var currentY: CGFloat = 0
    private var currentW: CGFloat = 0
    private var currentH: CGFloat = 0
    private var arrowPoint = CGPoint.zero
    private var maskPath = UIBezierPath()
    
    
    // MARK: - View
    override public func layoutSubviews() {
        super.layoutSubviews()
        switch style {
        case .base:
            contentView.frame = CGRect(
                x: 0,
                y: arrow.isUpward ? arrow.height +  2 * contentTBMargin : contentTBMargin,
                width: bounds.size.width,
                height: bounds.size.height - arrow.height - 2 * contentTBMargin
            )
        case .tableView:
            tableView.frame = CGRect(
                x: 0,
                y: arrow.isUpward ? arrow.height +  2 * contentTBMargin : contentTBMargin,
                width: bounds.size.width,
                height: bounds.size.height - arrow.height - 2 * contentTBMargin
            )
        }
    }
    
    // MARK: - Setup
    private func setup() {
        shadowView.isUserInteractionEnabled = hideAfterTouchOutside
        shadowView.backgroundColor = showShadow ? shadowColor : .clear
        backgroundColor = backgroundColor ?? .white
        shadowView.frame = keyWindow.bounds
        shadowView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(hideAction)))
        switch style {
        case .base:
            addSubview(contentView)
        case .tableView:
            addSubview(tableView)
            tableView.backgroundColor = backgroundColor
            tableView.isScrollEnabled = false
            tableView.separatorStyle = .none
            tableView.dataSource = self
            tableView.delegate = self
            tableView.register(PopoverCell.self, forCellReuseIdentifier: "PopoverCell")
        }
    }
    
    /// 显示弹窗
    ///
    /// - Parameter toPoint: 箭头指向的点
    private func show(toPoint: CGPoint) {
        setup()
        var point = toPoint
        // 如果箭头指向的点过于偏左或者过于偏右则需要重新调整箭头 x 轴的坐标
        let minHorizontalEdge = cornerRadius + arrow.width / 2 + viewMargin
        if point.x < minHorizontalEdge {
            point.x = minHorizontalEdge
        }
        if windowWidth - point.x < minHorizontalEdge {
            point.x = windowWidth - minHorizontalEdge
        }
        
        // 设置当前视图的 Frame
        currentW = viewWidth ?? calculateMaxWidth()
        if currentW > keyWindow.bounds.width - 2 * viewMargin {
            currentW = keyWindow.bounds.width - 2 * viewMargin
        }
        currentH = arrow.height + cellHeight * CGFloat(actions.count) + contentTBMargin * 2
        // 限制最大高度
        let maxHeight = arrow.isUpward ?
            windowHeight - point.y - arrow.margin - viewMargin :
            point.y - UIApplication.shared.statusBarFrame.height - arrow.margin - viewMargin
        // 如果弹窗高度大于设置的最大高度, 限制弹窗高度等于最大高度并允许 Table view 滑动
        if let h = viewHeight {
            if currentH > h {
                tableView.isScrollEnabled = true
                tableView.showsVerticalScrollIndicator = true
            } else {
                tableView.isScrollEnabled = false
                tableView.showsVerticalScrollIndicator = false
            }
            currentH = min(h, maxHeight)
        } else if currentH > maxHeight {
            currentH = maxHeight
            tableView.isScrollEnabled = true
            tableView.showsVerticalScrollIndicator = true
        } else {
            tableView.isScrollEnabled = false
            tableView.showsVerticalScrollIndicator = false
        }
        
        currentX = point.x - currentW / 2
        if point.x <= currentW / 2 { // 窗口过于靠左
            currentX = viewMargin
        }
        if windowWidth - point.x <= currentW / 2 { // 窗口靠右
            currentX = windowWidth - viewMargin - currentW
        }
        currentY = arrow.isUpward ? point.y + arrow.margin : point.y - arrow.margin - currentH
        frame = CGRect(x: currentX, y: currentY, width: currentW, height: currentH)
        switch style {
        case .base:
            break
        case .tableView:
            tableView.reloadData()
        }
        
        /// 绘制
        // 箭头顶点在当前视图的坐标
        arrowPoint = CGPoint(x: point.x - frame.minX, y: arrow.isUpward ? 0 : currentH)
        /// 顶部 Y 值
        let maskTop = arrow.isUpward ? arrow.height : 0
        /// 底部 Y 值
        let maskBottom = arrow.isUpward ? currentH : currentH - arrow.height
        maskPath = UIBezierPath()
        // 左上圆角
        maskPath.move(to: CGPoint(x: 0, y: cornerRadius + maskTop))
        maskPath.addArc(
            withCenter: CGPoint(x: cornerRadius, y: cornerRadius + maskTop),
            radius: cornerRadius,
            startAngle:  180 * CGFloat.pi / 180,
            endAngle: 270 * CGFloat.pi / 180,
            clockwise: true
        )
        // 箭头向上时的箭头位置
        if arrow.isUpward {
            maskPath.addLine(to: CGPoint(x: arrowPoint.x - arrow.width / 2, y: arrow.height))
            maskPath.addQuadCurve(
                to: CGPoint(x: arrowPoint.x - arrow.cornerRadius, y: arrow.cornerRadius),
                controlPoint: CGPoint(
                    x: arrowPoint.x - arrow.width / 2 + arrow.bottomCornerRadius,
                    y: arrow.height
                )
            )
            maskPath.addQuadCurve(
                to: CGPoint(
                    x: arrowPoint.x + arrow.cornerRadius,
                    y: arrow.cornerRadius
                ),
                controlPoint: arrowPoint
            )
            maskPath.addQuadCurve(
                to: CGPoint(
                    x: arrowPoint.x + arrow.width / 2,
                    y: arrow.height
                ),
                controlPoint: CGPoint(
                    x: arrowPoint.x + arrow.width / 2 - arrow.bottomCornerRadius,
                    y: arrow.height
                )
            )
        }
        // 右上圆角
        maskPath.addLine(to: CGPoint(x: currentW - cornerRadius, y: maskTop))
        maskPath.addArc(
            withCenter: CGPoint(
                x: currentW - cornerRadius,
                y: maskTop + cornerRadius
            ),
            radius: cornerRadius,
            startAngle: 270 * CGFloat.pi / 180,
            endAngle: 0,
            clockwise: true
        )
        // 右下圆角
        maskPath.addLine(to: CGPoint(x: currentW, y: maskBottom - cornerRadius))
        maskPath.addArc(
            withCenter: CGPoint(
                x: currentW - cornerRadius,
                y: maskBottom - cornerRadius
            ),
            radius: cornerRadius,
            startAngle: 0,
            endAngle: 90 * CGFloat.pi / 180,
            clockwise: true
        )
        // 箭头向下时的箭头位置
        if !arrow.isUpward {
            maskPath.addLine(
                to: CGPoint(x: arrowPoint.x + arrow.width / 2, y: currentH - arrow.height)
            )
            maskPath.addQuadCurve(
                to: CGPoint(
                    x: arrowPoint.x + arrow.cornerRadius,
                    y: currentH - arrow.cornerRadius
                ),
                controlPoint: CGPoint(
                    x: arrowPoint.x + arrow.width / 2 - arrow.bottomCornerRadius,
                    y: currentH - arrow.height
                )
            )
            maskPath.addQuadCurve(
                to: CGPoint(
                    x: arrowPoint.x - arrow.cornerRadius,
                    y: currentH - arrow.cornerRadius
                ),
                controlPoint: arrowPoint
            )
            maskPath.addQuadCurve(
                to: CGPoint(
                    x: arrowPoint.x - arrow.width / 2,
                    y: currentH - arrow.height
                ),
                controlPoint: CGPoint(
                    x: arrowPoint.x - arrow.width / 2 + arrow.bottomCornerRadius,
                    y: currentH - arrow.height
                )
            )
        }
        // 左下圆角
        maskPath.addLine(to: CGPoint(x: cornerRadius, y: maskBottom))
        maskPath.addArc(
            withCenter: CGPoint(x: cornerRadius, y: maskBottom - cornerRadius),
            radius: cornerRadius,
            startAngle: 90 * CGFloat.pi / 180,
            endAngle: 180 * CGFloat.pi / 180,
            clockwise: true
        )
        maskPath.close()
        
        // 遮罩层
        keyWindow.addSubview(shadowView)
        // 弹出动画
        keyWindow.addSubview(self)
        let oldFrame = frame
        // 截取圆角和箭头
        let maskLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = maskPath.cgPath
        layer.mask = maskLayer
        layer.anchorPoint = CGPoint(x: arrowPoint.x / currentW, y: arrow.isUpward ? 0 : 1)
        frame = oldFrame
        transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
        self.alpha = 0
        self.shadowView.alpha = 0
        UIView.animate(withDuration: 0.25) {
            self.transform = .identity
            self.alpha = 1
            self.shadowView.alpha = 1
        }
    }
    
    private func calculateMaxWidth() -> CGFloat {
        var maxWidth = CGFloat(0)
        for action in actions {
            let textWidth = NSString(string: action.title).size(
                withAttributes: [NSAttributedString.Key.font : textFont]
                ).width + 1
            if action.image != nil {
                if maxWidth < 20 + 16 + 10 + textWidth + 24 {
                    maxWidth = 20 + 16 + 10 + textWidth + 24
                }
            } else {
                if maxWidth < 20 + textWidth + 24 {
                    maxWidth = 20 + textWidth + 24
                }
            }
        }
        if maxWidth > windowWidth - 2 * viewMargin {
            maxWidth = windowWidth - 2 * viewMargin
        }
        return maxWidth
    }
    
    @objc private func hideAction() {
        hideClosure?()
        UIView.animate(withDuration: 0.25, animations: {
            self.alpha = 0
            self.shadowView.alpha = 0
            self.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
        }) { _ in
            self.shadowView.removeFromSuperview()
            self.removeFromSuperview()
        }
    }
    
    deinit {
        print("PopoverView - deinit")
    }
}

extension PopoverView: UITableViewDataSource, UITableViewDelegate {
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return actions.count
    }
    
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "PopoverCell") as! PopoverCell
        let action = actions[indexPath.row]
        cell.set(image: action.image, text: action.title, font: textFont, textColor: textColor, isShowLine: indexPath.row < actions.count - 1)
        return cell
    }
    
    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let action = actions[indexPath.row]
        action.handler?(action)
        hideAction()
    }
    
    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return cellHeight
    }
}

// MARK: - Base 风格 显示调用方法
extension PopoverView {
    /// 显示弹窗
    ///
    /// - Parameters:
    ///   - pointView: 目标视图
    ///   - content: 显示视图
    ///   - width: 视图宽度
    ///   - height: 视图高度
    ///   - arrowProintUp: 箭头是否指向上方
    public func show(to pointView: UIView, view content: UIView, width: CGFloat, height: CGFloat, arrowProintUp: Bool? = nil) {
        /// 相对与屏幕的相对区域
        guard let pointViewRect = pointView.superview?.convert(pointView.frame, to: keyWindow) else {
            return
        }
        /// 弹窗箭头指向的点
        var toPoint: CGPoint
        /// 视图顶部距屏幕顶部的高度
        let pointViewUpHeight = pointViewRect.minY
        /// 视图底部记屏幕底部的高度
        let pointViewDownHeight = windowHeight - pointViewRect.maxY
        if let pointAbove = arrowProintUp {
            if pointAbove {
                toPoint = CGPoint(x: pointViewRect.midX, y: pointViewRect.maxY + arrow.margin)
            } else {
                toPoint = CGPoint(x: pointViewRect.midX, y: pointViewUpHeight - arrow.margin)
            }
            show(to: toPoint, view: content, width: width, height: height, arrowProintUp: pointAbove)
        } else {
            /// 箭头指向方向
            var isUpward: Bool
            if pointViewUpHeight > pointViewDownHeight {
                toPoint = CGPoint(x: pointViewRect.midX, y: pointViewUpHeight - arrow.margin)
                isUpward = false
            } else {
                toPoint = CGPoint(x: pointViewRect.midX, y: pointViewRect.maxY + arrow.margin)
                isUpward = true
            }
            show(to: toPoint, view: content, width: width, height: height, arrowProintUp: isUpward)
        }
    }
    
    /// 显示弹窗
    ///
    /// - Parameters:
    ///   - point: 目标点
    ///   - content: 显示视图
    ///   - width: 视图宽度
    ///   - height: 视图高度
    ///   - arrowProintUp: 箭头是否指向上方
    public func show(to point: CGPoint, view content: UIView, width: CGFloat, height: CGFloat, arrowProintUp: Bool? = nil) {
        style = .base
        contentView = content
        viewWidth = width
        viewHeight = height + arrow.height + 2 * contentTBMargin
        if let isUpward = arrowProintUp {
            arrow.isUpward = isUpward
        } else {
            arrow.isUpward = point.y <= (windowHeight - point.y)
        }
        show(toPoint: point)
    }
}

// MARK: - Table view 风格显示调用方法
extension PopoverView {
    /// 显示弹窗
    ///
    /// - Parameters:
    ///   - pointView: 目标视图
    ///   - actions: 显示项
    ///   - arrowProintUp: 箭头视图指向上方
    public func show(to pointView: UIView, with actions: [PopoverAction], arrowProintUp: Bool? = nil) {
        /// 相对与屏幕的相对区域
        guard let pointViewRect = pointView.superview?.convert(pointView.frame, to: keyWindow) else {
            return
        }
        /// 弹窗箭头指向的点
        var toPoint: CGPoint
        /// 视图顶部距屏幕顶部的高度
        let pointViewUpHeight = pointViewRect.minY
        /// 视图底部记屏幕底部的高度
        let pointViewDownHeight = windowHeight - pointViewRect.maxY
        if let pointAbove = arrowProintUp {
            if pointAbove {
                toPoint = CGPoint(x: pointViewRect.midX, y: pointViewRect.maxY + arrow.margin)
            } else {
                toPoint = CGPoint(x: pointViewRect.midX, y: pointViewUpHeight - arrow.margin)
            }
            show(to: toPoint, with: actions, arrowProintUp: pointAbove)
        } else {
            /// 箭头指向方向
            var isUpward: Bool
            if pointViewUpHeight > pointViewDownHeight {
                toPoint = CGPoint(x: pointViewRect.midX, y: pointViewUpHeight - arrow.margin)
                isUpward = false
            } else {
                toPoint = CGPoint(x: pointViewRect.midX, y: pointViewRect.maxY + arrow.margin)
                isUpward = true
            }
            show(to: toPoint, with: actions, arrowProintUp: isUpward)
        }
    }
    
    /// 显示弹窗
    ///
    /// - Parameters:
    ///   - point: 目标点
    ///   - actions: 显示项
    ///   - arrowProintUp: 箭头视图指向上方
    public func show(to point: CGPoint, with actions: [PopoverAction], arrowProintUp: Bool? = nil) {
        self.style = .tableView
        self.actions = actions
        if let isUpward = arrowProintUp {
            arrow.isUpward = isUpward
        } else {
            arrow.isUpward = point.y <= (windowHeight - point.y)
        }
        show(toPoint: point)
    }
}
